//
//  STDBaseLayer.cpp
//  STDRenderSDK
//
//  Created by 陈学明 on 2023/3/15.
//

#include "STDBaseLayer.hpp"
#include <Effects/STDSEffectTool.h>
#include <glm/ext/matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale
#include <glm/ext/matrix_clip_space.hpp> // glm::perspective
#include <glm/ext/scalar_constants.hpp> // glm::pi

void STDBaseLayer::addEffect(int effect) {
    
    auto type = STDSEffectType(effect);
    for (auto item : effects) {
        if (type == item->mEffectType) {
            return;
        }
    }
    auto effectPainter = STDSEffectTool::getEffectPainter(type);
    if (effectPainter == nullptr) {
        return;
    }
    effectPainter->setContext(mContext);
    effects.push_back(effectPainter);
}

void STDBaseLayer::changeEffectFloatValue(int effect, const char *paramName, float *floatValue, int n) {
    auto type = STDSEffectType(effect);
    for (auto item : effects) {
        if (type == item->mEffectType) {
            item->setFloatArguments(paramName, floatValue, n);
            return;
        }
    }
}

void STDBaseLayer::changeEffectIntValue(int effect, const char *paramName, int *intValue, int n) {
    auto type = STDSEffectType(effect);
    for (auto item : effects) {
        if (type == item->mEffectType) {
            item->setIntArtuments(paramName, intValue, n);
            return;
        }
    }
}

void STDBaseLayer::changeEffectStringValue(int effect, const char *paramName, const char *stringValue) {
    auto type = STDSEffectType(effect);
    for (auto item : effects) {
        if (type == item->mEffectType) {
            item->setStringArtuments(paramName, stringValue);
            return;
        }
    }
}

void STDBaseLayer::changeEffectBitmap(int effect, const char *paramName, const char *data, int width, int height) {
    auto type = STDSEffectType(effect);
    for (auto item : effects) {
        if (type == item->mEffectType) {
            item->setBitmapArguments(paramName, data, width, height);
            return;
        }
    }
}

void STDBaseLayer::changeEffectTex(int effect, const char *paramName, unsigned int texId, int width, int height) {
    auto type = STDSEffectType(effect);
    for (auto item : effects) {
        if (type == item->mEffectType) {
            item->setTexIdArguments(paramName, texId, width, height);
            return;
        }
    }
}

void STDBaseLayer::removeEffect(int effect) {
    auto type = STDSEffectType(effect);
    vector<shared_ptr<STDSBaseEffect>>::iterator it = effects.begin();
    while (it != effects.end()) {
        auto item = *it;
        if (item->mEffectType == type) {
            effects.erase(it);
            mContext->autoRelease(item);
            return;
        }
        it ++;
    }
}

void STDBaseLayer::visibleEffect(int effect, int visible) {
    auto type = STDSEffectType(effect);
    for (auto item : effects) {
        if (type == item->mEffectType) {
            item->setVisible(visible != 0);
            return;
        }
    }
}

bool STDBaseLayer::checkEffectStatus(int effectType, const char *label) {
    auto type = STDSEffectType(effectType);
    for (auto item : effects) {
        if (type == item->mEffectType) {
            return item->checkStatus(label);
        }
    }
    return false;
}

void STDBaseLayer::clearEffects() {
    effects.clear();
}

void STDBaseLayer::addHotEffect(const char *effectName, const char *effectRoot) {
    for (auto item : effects) {
        if (STDSEffectTypeHotShader == item->mEffectType && strcmp(effectName, dynamic_pointer_cast<STDSHotShaderEffect>(item)->getEffectName().c_str())) {
            return;
        }
    }
    shared_ptr<STDSHotShaderEffect>effectPainter = make_shared<STDSHotShaderEffect>(string(effectName), string(effectRoot));
    effectPainter->setContext(mContext);
    effects.push_back(effectPainter);
}

void STDBaseLayer::removeHotEffect(const char *effectName) {
    vector<shared_ptr<STDSBaseEffect>>::iterator it = effects.begin();
    while (it != effects.end()) {
        auto item = *it;
        if (STDSEffectTypeHotShader == item->mEffectType && strcmp(effectName, dynamic_pointer_cast<STDSHotShaderEffect>(item)->getEffectName().c_str()) == 0) {
            effects.erase(it);
            mContext->autoRelease(item);
            return;
        }
        it ++;
    }
}

void STDBaseLayer::changeHotEffectFloatValue(const char *effectName, const char *paramName, float *floatValue, int n) {
    for (auto item : effects) {
        if (STDSEffectTypeHotShader == item->mEffectType && strcmp(effectName, dynamic_pointer_cast<STDSHotShaderEffect>(item)->getEffectName().c_str()) == 0) {
            item->setFloatArguments(paramName, floatValue, n);
            return;
        }
    }
}

void STDBaseLayer::changeHotEffectIntValue(const char *effectName, const char *paramName, int *intValue, int n) {
    for (auto item : effects) {
        if (STDSEffectTypeHotShader == item->mEffectType && strcmp(effectName, dynamic_pointer_cast<STDSHotShaderEffect>(item)->getEffectName().c_str()) == 0) {
            
            item->setIntArtuments(paramName, intValue, n);
            return;
        }
    }
}

void STDBaseLayer::changeHotEffectStringValue(const char *effectName, const char *paramName, const char *stringValue) {
    for (auto item : effects) {
        if (STDSEffectTypeHotShader == item->mEffectType && strcmp(effectName, dynamic_pointer_cast<STDSHotShaderEffect>(item)->getEffectName().c_str()) == 0) {
            item->setStringArtuments(paramName, stringValue);
            return;
        }
    }
}

void STDBaseLayer::visibleHotEffect(const char *effectName, int isVisible) {
    for (auto item : effects) {
        if (STDSEffectTypeHotShader == item->mEffectType && strcmp(effectName, dynamic_pointer_cast<STDSHotShaderEffect>(item)->getEffectName().c_str()) == 0) {
            item->setVisible(isVisible == 1);
            return;
        }
    }
}

void STDBaseLayer::addHotFilter(const char *filter, const char *effectRoot) {
    for (auto item : effects) {
        if (STDSEffectTypeGraphic == item->mEffectType && strcmp(filter, dynamic_pointer_cast<STDSGraphicEffect>(item)->getEffectName().c_str())) {
            return;
        }
    }
    shared_ptr<STDSGraphicEffect>effectPainter = make_shared<STDSGraphicEffect>(string(filter), string(effectRoot));
    effectPainter->setContext(mContext);
    effects.push_back(effectPainter);
}

void STDBaseLayer::removeHotFilter(const char *filter) {
    vector<shared_ptr<STDSBaseEffect>>::iterator it = effects.begin();
    while (it != effects.end()) {
        auto item = *it;
        if (STDSEffectTypeGraphic == item->mEffectType && strcmp(filter, dynamic_pointer_cast<STDSGraphicEffect>(item)->getEffectName().c_str()) == 0) {
            effects.erase(it);
            mContext->autoRelease(item);
            return;
        }
        it ++;
    }
}

void STDBaseLayer::visibleHotFilter(const char *filter, int isVisible) {
    for (auto item : effects) {
        if (STDSEffectTypeGraphic == item->mEffectType && strcmp(filter, dynamic_pointer_cast<STDSGraphicEffect>(item)->getEffectName().c_str()) == 0) {
            item->setVisible(isVisible == 1);
            return;
        }
    }
}

void STDBaseLayer::changeHotFilterFloatValue(const char *filter, const char *effectName, const char *paramName, float *floatValue, int n) {
    for (auto item : effects) {
        if (STDSEffectTypeGraphic == item->mEffectType && strcmp(filter, dynamic_pointer_cast<STDSGraphicEffect>(item)->getEffectName().c_str()) == 0) {
            dynamic_pointer_cast<STDSGraphicEffect>(item)->setFloatArguments(effectName, paramName, floatValue, n);
            return;
        }
    }
}

void STDBaseLayer::changeHotFilterIntValue(const char *filter, const char *effectName, const char *paramName, int *intValue, int n) {
    for (auto item : effects) {
        if (STDSEffectTypeGraphic == item->mEffectType && strcmp(filter, dynamic_pointer_cast<STDSGraphicEffect>(item)->getEffectName().c_str()) == 0) {
            dynamic_pointer_cast<STDSGraphicEffect>(item)->setIntArtuments(effectName, paramName, intValue, n);
            return;
        }
    }
}

void STDBaseLayer::changeHotFilterStringValue(const char *filter, const char *effectName, const char *paramName, const char *stringValue) {
    for (auto item : effects) {
        if (STDSEffectTypeGraphic == item->mEffectType && strcmp(filter, dynamic_pointer_cast<STDSGraphicEffect>(item)->getEffectName().c_str()) == 0) {
            dynamic_pointer_cast<STDSGraphicEffect>(item)->setStringArtuments(effectName, paramName, stringValue);
            return;
        }
    }
}

void STDBaseLayer::updataTransMat() {
    auto canvaseSize = mContext->getSizeSRC();
    float scaleX = mSizeSRC.x/canvaseSize.x;
    float scaleY = mSizeSRC.y/canvaseSize.y;
    vec2 center(2.0 * mCenter.x/canvaseSize.x - 1.0, 1.0 - 2.0 * mCenter.y/canvaseSize.y);
    auto transMat = glm::translate(glm::mat4(1.0f), vec3(center.x, center.y, 0.0));
    transMat = glm::rotate(transMat, mAngle, vec3(0.0, 0.0, 1.0));// 仰俯角
    transMat = glm::scale(transMat, vec3(scaleX * mScale.x, scaleY * mScale.y, 1.0));
    
    mMatV = transMat;
}

void STDBaseLayer::calculateAutoSize() {
    if (mTexture == nullptr) {
        return ;
    }
    auto canvaseSize = mContext->getSizeSRC();
    float viewP = canvaseSize.y/canvaseSize.x;
    float composeP = float(mTexture->getHeight())/float(mTexture->getWidth());
    vec2 dstSize;
    if (viewP > composeP) {
        dstSize.x = canvaseSize.x;
        dstSize.y = canvaseSize.x * composeP;
    } else {
        dstSize.x = canvaseSize.y/composeP;
        dstSize.y = canvaseSize.y;
    }
    mAutoSize = dstSize;
    if (mAutoH || mAutoW) {
        mSizeSRC = mAutoSize;
    }
}

void STDBaseLayer::transformationTranslation(float dx, float dy) {
    if (mAutoW || mAutoH) {
        mTransformMat = glm::translate(mTransformMat, vec3(dx*2.0/mAutoSize.x, dy*2.0/mAutoSize.y, 0.0));
    } else {
        mTransformMat = glm::translate(mTransformMat, vec3(dx*2.0/mSizeSRC.x, dy*2.0/mSizeSRC.y, 0.0));
    }
}

void STDBaseLayer::transformationScale(float dx, float dy) {
    mTransformMat = glm::scale(mTransformMat, vec3(1.0/mSizeSRC.x, 1.0/mSizeSRC.y, 1.0));
    mTransformMat = glm::scale(mTransformMat, vec3(dx, dy, 1.0));
    mTransformMat = glm::scale(mTransformMat, vec3(mSizeSRC.x, mSizeSRC.y, 1.0));
}

void STDBaseLayer::transformationRotation(float dAngle) {
    mTransformMat = glm::scale(mTransformMat, vec3(1.0/mSizeSRC.x, 1.0/mSizeSRC.y, 1.0));
    mTransformMat = glm::rotate(mTransformMat, dAngle, vec3(0.0, 0.0, 1.0));
    mTransformMat = glm::scale(mTransformMat, vec3(mSizeSRC.x, mSizeSRC.y, 1.0));
}

void STDBaseLayer::replaceImageAsset(int assetId, const char *path) {
    if (path == nullptr) {
        return;
    }
    string cppPath = string(path);
    if (mContext) {
        mTexture = mContext->getImageTexture(cppPath);
    }
    for (auto effect: effects) {
        effect->setTexture(mTexture);
    }
    calculateAutoSize();
    updataTransMat();
}

void STDBaseLayer::replaceImageTex(int assetId, const unsigned int textureId, float width, float height) {
    if (glIsTexture(textureId)) {
        mTexture = make_shared<STDSTexture>(textureId, width, height, false);
    }
    for (auto effect: effects) {
        effect->setTexture(mTexture);
    }
    calculateAutoSize();
    updataTransMat();
}

void STDBaseLayer::replaceImageData(int assetId, const unsigned char *data, float width, float height) {
    if (data == nullptr) {
        return;
    }
    mTexture = make_shared<STDSTexture>(data, width, height);
    for (auto effect: effects) {
        effect->setTexture(mTexture);
    }
    calculateAutoSize();
    updataTransMat();
}

void STDBaseLayer::replaceMaskAsset(int assetId, const char *path) {
    if (path == nullptr) {
        return;
    }
    string cppPath = string(path);
    if (mContext) {
        mMaskTexture = mContext->getImageTexture(cppPath);
    }
}

void STDBaseLayer::replaceMaskTex(int assetId, const unsigned int textureId, float width, float height) {
    if (glIsTexture(textureId)) {
        mMaskTexture = make_shared<STDSTexture>(textureId, width, height, false);
    }
}

void STDBaseLayer::replaceMaskData(int assetId, const unsigned char *data, float width, float height) {
    if (data == nullptr) {
        return;
    }
    mMaskTexture = make_shared<STDSTexture>(data, width, height);
}
